<html>

<head>
	<meta charset="UTF-8">
	<link rel="icon" type="image/png" href="img/favicon.ico">
	<title>串口助手</title>
	<link rel="stylesheet" href="../layui/css/layui.css">
	<script type="text/javascript" src="../layui/layui.js" charset="utf-8"></script>
	<script type="text/javascript" src="../static/echarts@5.3.3/echarts.js"></script>
	<script type="text/javascript" src="../static/jquery.min.js"></script>
</head>

<body>
	<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
		<legend>串口</legend>
	</fieldset>

	<div class="layui-tab layui-tab-card" lay-filter="demo">
		<ul class="layui-tab-title">
			<li class="layui-this">串口数据</li>
			<li>曲线</li>
		</ul>
		<div class="layui-tab-content">
			<div class="layui-tab-item layui-show">
				<div class="layui-row layui-col-space10">
					<div class="layui-col-xs8 layui-col-sm8 ">
						<div class="layui-input-block" style="margin-left:0px;">
							<textarea name="receive" placeholder="接收区" class="layui-textarea"
								style="height:390px;border:1px solid #0990D2;border-radius:10px;padding:10px;"
								id="receiveContent"></textarea>
						</div>
					</div>
					<div class="layui-col-xs4 layui-col-sm4 layui-col-md4">
						<div class="layui-form layui-form-item">
							<div class="layui-input-block layui-inline" style="margin-left:0px;">
								<select name="selectInterface" lay-verify="required" id="interfaceList"
									lay-filter="protList">

								</select>
							</div>
							<div class="layui-inline">
								<button type="button" class="layui-btn layui-btn-radius" id="Refresh">
									刷 新&nbsp;<i class="layui-icon layui-icon-refresh-3"></i>
								</button>
							</div>
						</div>
						<button class="layui-btn layui-btn-radius layui-btn-normal layui-btn-fluid"
							style="display:block;margin:5px 0px;" id="OpenSerial">打开串口</button>
						<button class="layui-btn layui-btn-radius layui-btn-warm layui-btn-fluid"
							style="display:block;margin:5px 0px;" id="clearReceiveDataBtn">清空接收区</button>

						<form class="layui-form layui-form-pane" action="">
							<div class="layui-form-item" pane="">
								<div>
									<input lay-filter="switchTest1" type="checkbox" name="like[write]"
										lay-skin="primary" title="十六进制接收" value="hexRec">
								</div>
							</div>
						</form>
					</div>
				</div>
				<div class="layui-row layui-col-space10">
					<div class="layui-tab layui-tab-card" lay-filter="demo1">
						<ul class="layui-tab-title">
							<li class="layui-this" style="font-size: 12px;min-width: 10px;min-height: 0px;">单条发送</li>
							<li style="font-size: 12px;min-width: 10px;min-height: 0px;">多条发送</li>
							<li style="font-size: 12px;min-width: 10px;min-height: 0px;">协议</li>
						</ul>
						<div class="layui-tab-content">
							<div class="layui-tab-item layui-show">
								<div class="layui-row layui-col-space10">
									<div class="layui-col-xs8 layui-col-sm8 layui-col-md8">
										<div class="layui-input-block" style="margin-left:0px;">
											<textarea name="receive" placeholder="发送区" class="layui-textarea"
												style="height:120px;border:1px solid #0990D2;border-radius:10px;padding:10px;"
												id="sendContent"></textarea>
										</div>
									</div>
									<div class="layui-col-xs4 layui-col-sm4 layui-col-md4">
										<button class="layui-btn layui-btn-radius layui-btn-normal layui-btn-fluid"
											style="display:block;margin:5px 0px;" id="sendDataBtn">发送</button>
										<button class="layui-btn layui-btn-radius layui-btn-danger layui-btn-fluid"
											style="display:block;margin:5px 0px;" id="clearSendDataBtn">清空发送区</button>
									</div>
								</div>

								<form class="layui-form layui-form-pane" action="">
									<div class="layui-form-item" pane="">
										<div>
											<div class="layui-inline">
												<div>
													<input lay-filter="switchTest" type="checkbox" name="like1[write]"
														lay-skin="primary" title="定时发送" value="timingSend">
												</div>
											</div>
											<div class="layui-inline" style="margin-left: 15px;">
												<label class="layui-form-label" style="width: 73px;">周期：</label>
												<div class="layui-input-inline" style="margin-left: 73px;">
													<input type="text" style="width: 60px;color: #0000FF;"
														name="username" lay-verify="required" placeholder="请输入"
														autocomplete="off" class="layui-input" id="time">
												</div>
											</div>
											<div class="layui-inline" style="margin-left: -135px;">
												<label>ms</label>
											</div>
										</div>
										<div>
											<input lay-filter="switchTest" type="checkbox" name="like1[write]"
												lay-skin="primary" title="十六进制发送" value="hexSend">
											<input lay-filter="switchTest" type="checkbox" name="like1[write]"
												lay-skin="primary" title="发送新行" value="sendNewLine">
										</div>
									</div>
								</form>

							</div>
							<div class="layui-tab-item">
								<button class="layui-btn layui-btn-radius layui-btn-danger layui-btn-fluid"
									style="display:block;margin:5px 0px;" id="clearSendDataBtn">清空发送区</button>
							</div>
						</div>
					</div>
				</div>
			</div>
			<div class="layui-tab-item" id="echarts" style="width: 800px;height:480px;"></div>
		</div>
	</div>

	<div hidden="hidden">
		<input id="ws" value={{.ws}} />
	</div>
</body>

<style>
	.layui-form-checkbox[lay-skin=primary] {
		height: 25px !important;
		line-height: normal !important;
		min-width: 18px;
		min-height: 18px;
		border: none !important;
		margin-right: 0;
		padding-left: 28px;
		padding-right: 0;
		background: 0 0
	}
</style>

<script>
	var index = 0;
	var date = [];
	var portList;
	var protName;
	var hexSend = false;
	var sendNewLine = false;
	var timingSend = false;
	var intervalFlag;
	var receiveHex = false;

	layui.use('element', function () {
		var element = layui.element;
		//一些事件监听
		element.on('tab(demo)', function (data) {
			index = data.index
			console.log(index);
		});
	});

	//一般直接写在一个js文件中
	layui.use(['form', 'layedit', 'laydate'], function () {
		var layer = layui.layer,
			form = layui.form;
		var $ = layui.$;

		var wsaddr = $("#ws").val();
		var ws = new WebSocket(wsaddr);
		console.log(ws);

		//连接打开时触发 
		ws.onopen = function (evt) {
			console.log("Connection open ...");
		};

		ws.binaryType = 'arraybuffer';

		ws.onerror = function (error) {
			layer.msg('WebSocket error');
		};

		ws.onmessage = function (evt) {
			if (index == 0) {
				if (receiveHex == true) {
					const uint8Array = new Uint8Array(event.data);

					// // 不带空格，小写字母
					// console.log(uint8ArrayToHex(uint8Array)); // 输出: "498036"

					// // 带空格，大写字母
					// console.log(uint8ArrayToHex(uint8Array, { spaced: true, upperCase: true })); // 输出: "49 80 36"

					// // 不带空格，大写字母
					// console.log(uint8ArrayToHex(uint8Array, { upperCase: true })); // 输出: "498036"

					// // 带空格，小写字母
					// console.log(uint8ArrayToHex(uint8Array, { spaced: true })); // 输出: "49 80 36"

					const hexString = uint8ArrayToHex(uint8Array, { spaced: true, upperCase: true });
					document.getElementById("receiveContent").value += hexString + " ";
				} else {
					const decoder = new TextDecoder();
					const stringData = decoder.decode(event.data);
					document.getElementById("receiveContent").value += stringData;
				}
			} else if (index == 1) {

				for (var i = 0; i < evt.data.length; i++) {
					data.push(evt.data.charCodeAt(i));
					console.log(evt.data.charCodeAt(i));
				}

				myChart.setOption({
					series: [{
						data: data
					}]
				});
			}
		};

		//连接关闭时触发  
		ws.onclose = function (evt) {
			console.log("Connection closed.");
		};
		// 发送数据
		$("#sendDataBtn").click(function () {
			var sendContent = $("#sendContent").val();
			if (hexSend == true) {
				// 去掉空格并确保字符串长度是偶数，否则最后会有一个不完整的字节
				let cleanedStr = sendContent.replace(/ /g, ''); // 使用正则表达式去除所有空白字符

				// 分割成长度为2的子串，并映射到对应的十进制值
				const byteArray = [];
				for (let i = 0; i < cleanedStr.length; i += 2) {
					byteArray.push(parseInt(cleanedStr.substr(i, 2), 16));
				}
				if (sendNewLine == true) {
					byteArray.push(13);
					byteArray.push(10);
				}

				const uint8Array = new Uint8Array(byteArray);
				ws.send(uint8Array);

			} else {
				if (sendNewLine == true) {
					sendContent += "\r\n"
				}
				ws.send(sendContent);
			}
		});
		// 清空发送区
		$("#clearSendDataBtn").click(function () {
			$("#sendContent").val('');
			layer.msg("清空发送区数据成功");
		});
		// 清空接收区
		$("#clearReceiveDataBtn").click(function () {
			$("#receiveContent").val('');
			layer.msg("清空发送区数据成功");
		});
		//刷新
		$("#Refresh").click(function () {
			handel();
			form.render("select");
			layer.msg("刷新串口");
		});
		$(function () {
			//加载刷新
			handel();
			form.render("select");
		})
		//打开串口
		$("#OpenSerial").click(function () {
			var serialData;
			if ($(this).text() == "打开串口") {
				serialData = "open"
			} else {
				serialData = "close"
			}
			$.ajax({
				url: "/OpenSerial",
				type: "POST",
				data: {
					"data": serialData,
					"portName": protName,
				},
				dataType: "json",
				success: function (rsp) {
					if (rsp.serilOpen == "OK") {
						layer.msg("打开串口");
						$("#OpenSerial").attr("class", "layui-btn layui-btn-radius layui-btn-fluid layui-btn-disabled");
						$("#OpenSerial").text("关闭串口");
					}
					if (rsp.serilClose == "OK") {
						layer.msg("关闭串口");
						$("#OpenSerial").attr("class", "layui-btn layui-btn-radius layui-btn-normal layui-btn-fluid");
						$("#OpenSerial").text("打开串口");
					}
				}
			})
		});

		//选择select数据
		form.on('select(protList)', function (data) {
			for (var x in portList) {
				if (portList[x].id == data.value) {
					protName = portList[x].proNo
					layer.msg(protName);
				}
			}
		});
		var t = 0;
		var r = /[1-9]+\d*/;
		$('#time').on('input propertychange', function () {
			// console.log($('#time').val());  
			layer.msg($('#time').val());
			if (r.test($("#time").val())) {
				t = $("#time").val();
			}
			if (timingSend == true) {
				clearInterval(intervalFlag);
				intervalFlag = setInterval(timeSend, t);
			}
		});

		//监听多选框点击事件  主要是通过 lay-filter="switchTest"  来监听
		form.on('checkbox(switchTest)', function (data) {
			if (data.elem.checked) { //判断当前多选框是选中还是取消选中
				if ($(this).val() == "timingSend") {
					timingSend = true;
					intervalFlag = setInterval(timeSend, t);
				}
				else if ($(this).val() == "sendNewLine") {
					sendNewLine = true;
					if (hexSend == true) {
						var sendContent = $("#sendContent").val();
						console.log(sendContent);
					}
				}
				else if ($(this).val() == "hexSend") {
					hexSend = true;
					$("#sendContent").val(stringToHex($("#sendContent").val()));
				}
			} else {
				if ($(this).val() == "timingSend") {
					timingSend = false;
					clearInterval(intervalFlag);
				}
				else if ($(this).val() == "sendNewLine") {
					sendNewLine = false;
				}
				else if ($(this).val() == "hexSend") {
					hexSend = false;
					$("#sendContent").val(hexToString($("#sendContent").val()));
				}
			}
		});

		form.on('checkbox(switchTest1)', function (data) {
			// console.log(data.elem); // 得到当前复选框对象
			// console.log(data.elem.checked); // 是否被选中
			// console.log(data.value); // 复选框值
			if (data.elem.checked) {
				if ($(this).val() == "hexRec") {
					receiveHex = true;
					$("#receiveContent").val(stringToHex($("#receiveContent").val()));
				}
			} else {
				if ($(this).val() == "hexRec") {
					receiveHex = false;
					$("#receiveContent").val(hexToString($("#receiveContent").val()));
				}
			}
		});

		function timeSend() {
			var sendContent = $("#sendContent").val()
			if (sendNewLine == true) {
				sendContent += "\r\n"
			}
			ws.send(sendContent);
		}
	});
</script>

<script>
	function handel() {
		var htmls = '<option value="">请选择</option>'; //全局变量
		$.ajax({
			url: '/portlist',
			type: "post",
			dataType: "json",
			contentType: "application/json",
			async: false, //这得注意是同步
			success: function (result) {
				portList = result;
				console.log(portList);
				for (var x in result) {
					htmls += '<option value = "' + result[x].id + '">' + result[x].proNo + '</option>'
				}
				$("#interfaceList").html(htmls);
			}
		});
	}

	function stringToHex(str) {
		var val = "";
		for (var i = 0; i < str.length; i++) {
			if (str[i] === "\n") {
				// 如果是换行符 \n，检查前一个字符是否不是回车符 \r
				if (i === 0 || str[i - 1] !== "\r") {
					val += "0D 0A"; // 将 \n 转换为 \r\n
				} else {
					// 如果前面有 \r，则仅追加 0A
					val += "0A";
				}
			} else if (str[i] === "\r") {
				// 如果是回车符 \r，直接追加 0D
				val += "0D";
			} else {
				// 对于其他字符，转换为十六进制并追加到结果字符串
				val += str.charCodeAt(i).toString(16).toUpperCase().padStart(2, '0');
			}
			if (i < str.length - 1) val += " "; // 在字符之间添加空格
		}
		val += " ";
		return val;
	}

	function hexToString(hexCharCodeStr) {
		hexCharCodeStr = hexCharCodeStr.replace(/\s+/g, ""); //去掉空格
		var trimedStr = hexCharCodeStr.trim();
		var rawStr =
			trimedStr.substr(0, 2).toLowerCase() === "0x" ?
				trimedStr.substr(2) :
				trimedStr;
		var len = rawStr.length;
		if (len % 2 !== 0) {
			alert("Illegal Format ASCII Code!");
			return "";
		}
		var curCharCode;
		var resultStr = [];
		for (var i = 0; i < len; i = i + 2) {
			curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
			resultStr.push(String.fromCharCode(curCharCode));
		}
		return resultStr.join("");
	}

	function uint8ArrayToHex(uint8Array, options = { spaced: false, upperCase: false }) {
		const { spaced, upperCase } = options;
		return Array.from(uint8Array, byte =>
			('0' + (byte & 0xFF).toString(16)).slice(-2)
		)
			.map(hex => upperCase ? hex.toUpperCase() : hex) // 如果upperCase为true，则转为大写
			.join(spaced ? ' ' : ''); // 如果spaced为true，则用空格分隔
	}
</script>

</html>